#!/usr/bin/env python
# -*- coding: UTF-8 -*-
'''
@Project ：V2 
@File    ：DB4.py
@IDE     ：PyCharm 
@Author  ：郭星
@Date    ：2025/9/23 20:42 
'''
import random
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.font_manager import FontProperties
from typing import List, Tuple, Dict

# 修复中文显示问题
try:
    font = FontProperties(family=['SimHei', 'SimHei', 'SimHei', 'SimHei', 'SimHei'])
    plt.rcParams["font.family"] = ["SimHei", "SimHei", "SimHei", "SimHei", "SimHei"]
    plt.rcParams["axes.unicode_minus"] = False
except:
    print("警告：未找到中文字体，可能会导致中文显示异常")
    plt.rcParams["axes.unicode_minus"] = False


class GasSchedulingGA:
    def __init__(self,
                 num_workpieces: int,
                 num_components: int,
                 num_inflation_eq: int,
                 num_analysis_eq_per_component: int,
                 population_size: int = 50,
                 generations: int = 100,
                 crossover_rate: float = 0.8,
                 mutation_rate: float = 0.1):
        """初始化气体调度遗传算法"""
        self.num_workpieces = num_workpieces
        self.num_components = num_components
        self.num_inflation_eq = num_inflation_eq
        self.num_analysis_eq_per_component = num_analysis_eq_per_component

        self.population_size = population_size
        self.generations = generations
        self.crossover_rate = crossover_rate
        self.mutation_rate = mutation_rate

        # 随机生成加工时间（实际应用中替换为真实数据）
        self.inflation_time = np.random.randint(5, 15, size=num_workpieces)
        self.analysis_time = np.random.randint(3, 10, size=(num_workpieces, num_components))

        self.population = []
        self.generation_best_time = []  # 记录每代最佳解的最大完成时间

    def initialize_chromosome(self) -> Dict:
        """初始化一条染色体，支持随机组分数（1到最大组分数之间）"""
        # 充气阶段基因
        inflation_order = random.sample(range(self.num_workpieces), self.num_workpieces)
        inflation_eq = [random.randint(0, self.num_inflation_eq - 1) for _ in range(self.num_workpieces)]

        # 分析阶段基因
        analysis_order = random.sample(range(self.num_workpieces), self.num_workpieces)

        # 组分串联组合基因 - 修改部分
        component_groups = []
        group_test_order = []
        analysis_eq = []
        for _ in range(self.num_workpieces):
            # 对于每个工件，随机选择最大组分数（1到总组分数之间）
            max_possible_groups = random.randint(1, self.num_components)
            # 随机选择实际使用的组数量（1到最大可能组分数之间）
            actual_groups = random.randint(1, max_possible_groups)

            # 为每个组分分配组号（0到actual_groups-1之间）
            groups = [random.randint(0, actual_groups - 1) for _ in range(self.num_components)]

            component_groups.append(groups)

            # 串联组测试顺序（基于实际使用的组）
            unique_groups = list(sorted(set(groups)))  # 只包含实际使用的组
            random.shuffle(unique_groups)
            group_test_order.append(unique_groups)

            # 生成分析设备选择
            eq_for_workpiece = []
            for group_id in unique_groups:
                components_in_group = [j for j, g in enumerate(groups) if g == group_id]
                eq_for_group = [random.randint(0, self.num_analysis_eq_per_component - 1)
                                for _ in components_in_group]
                eq_for_workpiece.append(eq_for_group)
            analysis_eq.append(eq_for_workpiece)

        return {
            'inflation_order': inflation_order,
            'inflation_eq': inflation_eq,
            'analysis_order': analysis_order,
            'component_groups': component_groups,
            'group_test_order': group_test_order,
            'analysis_eq': analysis_eq
        }

    def initialize_population(self):
        """初始化种群"""
        self.population = [self.initialize_chromosome() for _ in range(self.population_size)]

    def calculate_fitness(self, chromosome: Dict) -> Tuple[float, float]:
        """计算适应度和最大完成时间"""
        # 计算充气阶段时间
        inflation_machine_time = [0] * self.num_inflation_eq
        for workpiece in chromosome['inflation_order']:
            eq = chromosome['inflation_eq'][workpiece]
            start_time = inflation_machine_time[eq]
            end_time = start_time + self.inflation_time[workpiece]
            inflation_machine_time[eq] = end_time
        inflation_completion_time = max(inflation_machine_time)

        # 计算分析阶段时间
        analysis_machine_time = [[0 for _ in range(self.num_analysis_eq_per_component)]
                                 for _ in range(self.num_components)]
        workpiece_inflation_end = {}
        temp_machine_time = [0] * self.num_inflation_eq
        for workpiece in chromosome['inflation_order']:
            eq = chromosome['inflation_eq'][workpiece]
            start_time = temp_machine_time[eq]
            end_time = start_time + self.inflation_time[workpiece]
            temp_machine_time[eq] = end_time
            workpiece_inflation_end[workpiece] = end_time

        # 按分析顺序处理工件
        for workpiece in chromosome['analysis_order']:
            if workpiece < 0 or workpiece >= self.num_workpieces:
                continue

            start_analysis_time = workpiece_inflation_end.get(workpiece, 0)
            group_test_order = chromosome['group_test_order'][workpiece]
            analysis_eq = chromosome['analysis_eq'][workpiece]

            for group_idx, group_id in enumerate(group_test_order):
                if group_idx < 0 or group_idx >= len(analysis_eq):
                    continue

                components_in_group = [j for j, g in enumerate(chromosome['component_groups'][workpiece])
                                       if g == group_id]
                eq_for_group = analysis_eq[group_idx]

                if len(eq_for_group) != len(components_in_group):
                    eq_for_group = [random.randint(0, self.num_analysis_eq_per_component - 1)
                                    for _ in components_in_group]
                    analysis_eq[group_idx] = eq_for_group

                # 计算组准备时间
                group_ready_time = start_analysis_time
                for comp_idx, component in enumerate(components_in_group):
                    if comp_idx < 0 or comp_idx >= len(eq_for_group):
                        continue
                    eq = eq_for_group[comp_idx]
                    group_ready_time = max(group_ready_time, analysis_machine_time[component][eq])

                # 计算串联组的总时间（取最长的单个测试时间）
                max_analysis_time = max(
                    self.analysis_time[workpiece][component]
                    for comp_idx, component in enumerate(components_in_group)
                    if comp_idx < len(eq_for_group)
                ) if components_in_group else 0

                # 所有串联设备的结束时间相同
                group_end_time = group_ready_time + max_analysis_time

                # 更新所有串联设备的时间
                for comp_idx, component in enumerate(components_in_group):
                    if comp_idx < 0 or comp_idx >= len(eq_for_group):
                        continue
                    eq = eq_for_group[comp_idx]
                    analysis_machine_time[component][eq] = group_end_time

                start_analysis_time = group_end_time

        analysis_completion_time = max(max(machine_times) for machine_times in analysis_machine_time)
        max_time = max(inflation_completion_time, analysis_completion_time)
        return 1.0 / max_time, max_time

    def get_schedule_details(self, chromosome: Dict) -> Tuple[Dict, Dict]:
        """获取调度详细信息，用于绘制甘特图"""
        # 充气阶段详细信息
        inflation_details = {}
        for eq in range(self.num_inflation_eq):
            inflation_details[eq] = []

        inflation_machine_time = [0] * self.num_inflation_eq
        for workpiece in chromosome['inflation_order']:
            eq = chromosome['inflation_eq'][workpiece]
            start_time = inflation_machine_time[eq]
            end_time = start_time + self.inflation_time[workpiece]
            inflation_machine_time[eq] = end_time
            inflation_details[eq].append({
                'workpiece': workpiece,
                'start': start_time,
                'end': end_time
            })

        # 分析阶段详细信息
        analysis_details = {}
        for component in range(self.num_components):
            analysis_details[component] = {}
            for eq in range(self.num_analysis_eq_per_component):
                analysis_details[component][eq] = []

        workpiece_inflation_end = {}
        temp_machine_time = [0] * self.num_inflation_eq
        for workpiece in chromosome['inflation_order']:
            eq = chromosome['inflation_eq'][workpiece]
            start_time = temp_machine_time[eq]
            end_time = start_time + self.inflation_time[workpiece]
            temp_machine_time[eq] = end_time
            workpiece_inflation_end[workpiece] = end_time

        analysis_machine_time = [[0 for _ in range(self.num_analysis_eq_per_component)]
                                 for _ in range(self.num_components)]

        for workpiece in chromosome['analysis_order']:
            if workpiece < 0 or workpiece >= self.num_workpieces:
                continue

            start_analysis_time = workpiece_inflation_end.get(workpiece, 0)
            group_test_order = chromosome['group_test_order'][workpiece]
            analysis_eq = chromosome['analysis_eq'][workpiece]

            for group_idx, group_id in enumerate(group_test_order):
                if group_idx < 0 or group_idx >= len(analysis_eq):
                    continue

                components_in_group = [j for j, g in enumerate(chromosome['component_groups'][workpiece])
                                       if g == group_id]
                eq_for_group = analysis_eq[group_idx]

                if len(eq_for_group) != len(components_in_group):
                    eq_for_group = [random.randint(0, self.num_analysis_eq_per_component - 1)
                                    for _ in components_in_group]

                # 计算组准备时间
                group_ready_time = start_analysis_time
                for comp_idx, component in enumerate(components_in_group):
                    if comp_idx < 0 or comp_idx >= len(eq_for_group):
                        continue
                    eq = eq_for_group[comp_idx]
                    group_ready_time = max(group_ready_time, analysis_machine_time[component][eq])

                # 计算串联组的总时间
                max_analysis_time = max(
                    self.analysis_time[workpiece][component]
                    for comp_idx, component in enumerate(components_in_group)
                    if comp_idx < len(eq_for_group)
                ) if components_in_group else 0

                group_end_time = group_ready_time + max_analysis_time

                # 更新所有串联设备的时间并记录详细信息
                for comp_idx, component in enumerate(components_in_group):
                    if comp_idx < 0 or comp_idx >= len(eq_for_group):
                        continue
                    eq = eq_for_group[comp_idx]
                    analysis_machine_time[component][eq] = group_end_time
                    analysis_details[component][eq].append({
                        'workpiece': workpiece,
                        'start': group_ready_time,
                        'end': group_end_time,
                        'group': group_id,
                        'individual_time': self.analysis_time[workpiece][component]
                    })

                start_analysis_time = group_end_time

        return inflation_details, analysis_details

    def select_parents(self, fitness_values: List[float]) -> Tuple[Dict, Dict]:
        """轮盘赌选择父母"""
        total_fitness = sum(fitness_values)
        probabilities = [f / total_fitness for f in fitness_values]

        parent1_idx = np.random.choice(self.population_size, p=probabilities)
        parent2_idx = np.random.choice(self.population_size, p=probabilities)
        while parent1_idx == parent2_idx:
            parent2_idx = np.random.choice(self.population_size, p=probabilities)

        return self.population[parent1_idx], self.population[parent2_idx]

    def crossover(self, parent1: Dict, parent2: Dict) -> Tuple[Dict, Dict]:
        """交叉操作"""
        if random.random() > self.crossover_rate:
            return parent1.copy(), parent2.copy()

        # 顺序交叉
        def order_crossover(order1, order2):
            size = len(order1)
            a, b = random.sample(range(size), 2)
            if a > b:
                a, b = b, a

            child1 = [-1] * size
            child2 = [-1] * size
            child1[a:b + 1] = order1[a:b + 1]
            child2[a:b + 1] = order2[a:b + 1]

            def fill_child(child, parent, start, end):
                ptr = (end + 1) % size
                for i in range(end + 1, end + 1 + size):
                    current = parent[i % size]
                    if current not in child:
                        child[ptr] = current
                        ptr = (ptr + 1) % size
                return child

            child1 = fill_child(child1, order2, a, b)
            child2 = fill_child(child2, order1, a, b)
            return child1, child2

        # 交叉充气顺序和分析顺序
        child1_inflation_order, child2_inflation_order = order_crossover(
            parent1['inflation_order'], parent2['inflation_order']
        )
        child1_analysis_order, child2_analysis_order = order_crossover(
            parent1['analysis_order'], parent2['analysis_order']
        )

        # 深拷贝确保子染色体独立
        child1 = {
            'inflation_order': child1_inflation_order,
            'inflation_eq': parent1['inflation_eq'].copy(),
            'analysis_order': child1_analysis_order,
            'component_groups': [g.copy() for g in parent1['component_groups']],
            'group_test_order': [g.copy() for g in parent1['group_test_order']],
            'analysis_eq': [g.copy() for g in parent1['analysis_eq']]
        }
        for i in range(len(child1['analysis_eq'])):
            child1['analysis_eq'][i] = [e.copy() for e in child1['analysis_eq'][i]]

        child2 = {
            'inflation_order': child2_inflation_order,
            'inflation_eq': parent2['inflation_eq'].copy(),
            'analysis_order': child2_analysis_order,
            'component_groups': [g.copy() for g in parent2['component_groups']],
            'group_test_order': [g.copy() for g in parent2['group_test_order']],
            'analysis_eq': [g.copy() for g in parent2['analysis_eq']]
        }
        for i in range(len(child2['analysis_eq'])):
            child2['analysis_eq'][i] = [e.copy() for e in child2['analysis_eq'][i]]

        # 充气设备选择交叉
        for i in range(self.num_workpieces):
            if random.random() < 0.5:
                child1['inflation_eq'][i] = parent2['inflation_eq'][i]
                child2['inflation_eq'][i] = parent1['inflation_eq'][i]

        # 组分分组交叉
        for i in range(self.num_workpieces):
            if random.random() < 0.5:
                # 交叉组分分组
                child1['component_groups'][i] = parent2['component_groups'][i].copy()
                child2['component_groups'][i] = parent1['component_groups'][i].copy()

                # 重新计算组测试顺序和设备列表
                # 对child1处理
                groups1 = child1['component_groups'][i]
                unique_groups1 = list(sorted(set(groups1)))
                random.shuffle(unique_groups1)
                child1['group_test_order'][i] = unique_groups1
                # 重新生成设备列表确保长度匹配
                new_eq1 = []
                for group_id in unique_groups1:
                    components = [j for j, g in enumerate(groups1) if g == group_id]
                    new_eq1.append([random.randint(0, self.num_analysis_eq_per_component - 1)
                                    for _ in components])
                child1['analysis_eq'][i] = new_eq1

                # 对child2处理
                groups2 = child2['component_groups'][i]
                unique_groups2 = list(sorted(set(groups2)))
                random.shuffle(unique_groups2)
                child2['group_test_order'][i] = unique_groups2
                # 重新生成设备列表确保长度匹配
                new_eq2 = []
                for group_id in unique_groups2:
                    components = [j for j, g in enumerate(groups2) if g == group_id]
                    new_eq2.append([random.randint(0, self.num_analysis_eq_per_component - 1)
                                    for _ in components])
                child2['analysis_eq'][i] = new_eq2

        return child1, child2

    def mutate(self, chromosome: Dict) -> Dict:
        """变异操作"""
        # 深拷贝染色体
        mutated = {
            'inflation_order': chromosome['inflation_order'].copy(),
            'inflation_eq': chromosome['inflation_eq'].copy(),
            'analysis_order': chromosome['analysis_order'].copy(),
            'component_groups': [g.copy() for g in chromosome['component_groups']],
            'group_test_order': [g.copy() for g in chromosome['group_test_order']],
            'analysis_eq': [g.copy() for g in chromosome['analysis_eq']]
        }
        for i in range(len(mutated['analysis_eq'])):
            mutated['analysis_eq'][i] = [e.copy() for e in mutated['analysis_eq'][i]]

        # 充气顺序变异
        if random.random() < self.mutation_rate:
            idx1, idx2 = random.sample(range(self.num_workpieces), 2)
            mutated['inflation_order'][idx1], mutated['inflation_order'][idx2] = \
                mutated['inflation_order'][idx2], mutated['inflation_order'][idx1]

        # 分析顺序变异
        if random.random() < self.mutation_rate:
            idx1, idx2 = random.sample(range(self.num_workpieces), 2)
            mutated['analysis_order'][idx1], mutated['analysis_order'][idx2] = \
                mutated['analysis_order'][idx2], mutated['analysis_order'][idx1]

        # 充气设备选择变异
        for i in range(self.num_workpieces):
            if random.random() < self.mutation_rate:
                mutated['inflation_eq'][i] = random.randint(0, self.num_inflation_eq - 1)

        # 组分分组变异 - 支持随机改变组数量
        for i in range(self.num_workpieces):
            if random.random() < self.mutation_rate:
                # 随机选择新的实际组数量
                new_actual_groups = random.randint(1, self.num_components)
                # 为每个组分重新分配组号
                mutated['component_groups'][i] = [
                    random.randint(0, new_actual_groups - 1)
                    for _ in range(self.num_components)
                ]

                # 更新组测试顺序和设备列表
                unique_groups = list(sorted(set(mutated['component_groups'][i])))
                random.shuffle(unique_groups)
                mutated['group_test_order'][i] = unique_groups

                # 重新生成分析设备选择
                new_analysis_eq = []
                for group_id in unique_groups:
                    components_in_group = [j for j, g in enumerate(mutated['component_groups'][i]) if g == group_id]
                    new_analysis_eq.append([
                        random.randint(0, self.num_analysis_eq_per_component - 1)
                        for _ in components_in_group
                    ])
                mutated['analysis_eq'][i] = new_analysis_eq

        # 分析设备选择变异
        for i in range(self.num_workpieces):
            for group_idx in range(len(mutated['group_test_order'][i])):
                if random.random() < self.mutation_rate:
                    eq_list = mutated['analysis_eq'][i][group_idx]
                    if len(eq_list) > 0:
                        comp_idx = random.randint(0, len(eq_list) - 1)
                        eq_list[comp_idx] = random.randint(0, self.num_analysis_eq_per_component - 1)

        return mutated

    def evolve(self) -> Tuple[Dict, float, float]:
        """执行遗传算法进化过程"""
        self.initialize_population()
        self.generation_best_time = []
        best_fitness = 0
        best_time = float('inf')
        best_chromosome = None

        for generation in range(self.generations):
            try:
                fitness_and_time = [self.calculate_fitness(chrom) for chrom in self.population]
                fitness_values = [ft[0] for ft in fitness_and_time]
                time_values = [ft[1] for ft in fitness_and_time]
            except Exception as e:
                print(f"计算适应度时出错: {e}")
                self.initialize_population()
                fitness_and_time = [self.calculate_fitness(chrom) for chrom in self.population]
                fitness_values = [ft[0] for ft in fitness_and_time]
                time_values = [ft[1] for ft in fitness_and_time]

            current_best_idx = np.argmax(fitness_values)
            current_best_fitness = fitness_values[current_best_idx]
            current_best_time = time_values[current_best_idx]
            current_best_chromosome = self.population[current_best_idx]

            self.generation_best_time.append(current_best_time)

            if current_best_fitness > best_fitness:
                best_fitness = current_best_fitness
                best_time = current_best_time
                best_chromosome = current_best_chromosome

            if generation % 10 == 0:
                print(f"Generation {generation}: Best Time = {current_best_time:.2f}")

        return best_chromosome, best_fitness, best_time

    def plot_convergence_curve(self):
        """绘制最大完成时间的收敛曲线"""
        plt.figure(figsize=(10, 6))
        plt.plot(range(1, self.generations + 1), self.generation_best_time, 'r-', linewidth=2)
        plt.xlabel('进化代数', fontproperties=font)
        plt.ylabel('最大完成时间', fontproperties=font)
        plt.title('最大完成时间收敛曲线', fontproperties=font)
        plt.grid(True, linestyle='--', alpha=0.7)

        final_time = self.generation_best_time[-1]
        plt.annotate(f'最终时间: {final_time:.2f}',
                     xy=(self.generations, final_time),
                     xytext=(self.generations * 0.8, final_time * 1.05),
                     arrowprops=dict(facecolor='black', shrink=0.05),
                     fontproperties=font)

        plt.tight_layout()
        plt.show()

    def plot_gantt_chart(self, chromosome: Dict):
        """绘制甘特图展示调度方案"""
        inflation_details, analysis_details = self.get_schedule_details(chromosome)

        # 生成颜色列表
        colors = plt.cm.tab10(np.linspace(0, 1, self.num_workpieces))

        # 创建画布
        fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8 + self.num_components * 1.5))
        fig.suptitle('气体调度甘特图', fontproperties=font, fontsize=16)

        # 绘制充气阶段甘特图
        y_ticks = []
        y_labels = []

        for i, eq in enumerate(inflation_details.keys()):
            y_pos = i + 1
            y_ticks.append(y_pos)
            y_labels.append(f'充气设备 {eq}')

            for task in inflation_details[eq]:
                workpiece = task['workpiece']
                start = task['start']
                end = task['end']
                ax1.barh(y_pos, end - start, left=start, height=0.6,
                         color=colors[workpiece], edgecolor='black')
                ax1.text(start + (end - start) / 2, y_pos, f'瓶{workpiece}',
                         ha='center', va='center', color='white', fontweight='bold', fontproperties=font)

        ax1.set_yticks(y_ticks)
        ax1.set_yticklabels(y_labels, fontproperties=font)
        ax1.set_xlabel('时间', fontproperties=font)
        ax1.set_title('充气阶段', fontproperties=font)
        ax1.grid(True, axis='x', linestyle='--', alpha=0.7)

        # 绘制分析阶段甘特图
        y_ticks = []
        y_labels = []
        current_y = 1

        # 收集所有组信息
        all_groups = {}

        for component in sorted(analysis_details.keys()):
            for eq in sorted(analysis_details[component].keys()):
                y_pos = current_y
                y_ticks.append(y_pos)
                y_labels.append(f'组分{component}分析设备 {eq}')

                for task in analysis_details[component][eq]:
                    workpiece = task['workpiece']
                    start = task['start']
                    end = task['end']
                    group_id = task['group']
                    individual_time = task['individual_time']

                    group_key = (workpiece, group_id)
                    if group_key not in all_groups:
                        all_groups[group_key] = []
                    all_groups[group_key].append((y_pos, start, end))

                    ax2.barh(y_pos, end - start, left=start, height=0.6,
                             color=colors[workpiece], edgecolor='black')
                    label = f'瓶{workpiece}({individual_time})'
                    ax2.text(start + (end - start) / 2, y_pos, label,
                             ha='center', va='center', color='white', fontweight='bold',
                             fontsize=8, fontproperties=font)

                current_y += 1

            current_y += 0.5

        # 绘制串联组连接线
        for group_key, tasks in all_groups.items():
            if len(tasks) > 1:
                workpiece, group_id = group_key
                mid_time = (tasks[0][1] + tasks[0][2]) / 2
                y_positions = [task[0] for task in tasks]
                ax2.plot([mid_time, mid_time], [min(y_positions) - 0.3, max(y_positions) + 0.3],
                         'k--', linewidth=1.5, alpha=0.7)
                ax2.text(mid_time, max(y_positions) + 0.4, f'串联组{group_id}',
                         ha='center', va='bottom', color='black', fontsize=7,
                         fontweight='bold', fontproperties=font)

        ax2.set_yticks(y_ticks)
        ax2.set_yticklabels(y_labels, fontproperties=font)
        ax2.set_xlabel('时间', fontproperties=font)
        ax2.set_title('分析阶段（虚线连接表示串联测试）', fontproperties=font)
        ax2.grid(True, axis='x', linestyle='--', alpha=0.7)

        # 添加图例
        legend_items = [mpatches.Patch(color=colors[i], label=f'气瓶 {i}')
                        for i in range(self.num_workpieces)]
        fig.legend(handles=legend_items, loc='lower center', ncol=self.num_workpieces,
                   bbox_to_anchor=(0.5, -0.02), prop=font)

        plt.tight_layout(rect=[0, 0.05, 1, 0.95])
        plt.show()


# 示例用法
if __name__ == "__main__":
    num_workpieces = 50  # 5个气瓶
    num_components = 5  # 每个气瓶有3种组分
    num_inflation_eq = 4  # 2台充气设备
    num_analysis_eq = 5  # 每种组分有2台分析设备

    ga = GasSchedulingGA(
        num_workpieces=num_workpieces,
        num_components=num_components,
        num_inflation_eq=num_inflation_eq,
        num_analysis_eq_per_component=num_analysis_eq,
        population_size=20,
        generations=600,
        crossover_rate=0.8,
        mutation_rate=0.1
    )

    best_solution, best_fitness, best_time = ga.evolve()

    print("\n最佳调度方案:")
    print(f"最大完成时间: {best_time:.2f}")
    print("充气顺序:", best_solution['inflation_order'])
    print("充气设备分配:", best_solution['inflation_eq'])
    print("分析顺序:", best_solution['analysis_order'])

    print("\n组分分组情况（每行代表一个气瓶，数字表示组号）:")
    for i, groups in enumerate(best_solution['component_groups']):
        # 统计每个气瓶实际使用的组数量
        group_count = len(set(groups))
        print(f"气瓶 {i}: {groups} (实际使用 {group_count} 个组)")

    # 绘制收敛曲线
    ga.plot_convergence_curve()

    # 绘制甘特图
    ga.plot_gantt_chart(best_solution)
